#include "Util.h"
#include "../../include/Logger.h"
#include <fstream>
#include <sys/stat.h>

// 1. 分隔字符串
size_t Util::Split(const std::string &src, const std::string &sep, std::vector<std::string> *array)
{
    size_t offset = 0;
    while (offset <= src.size())
    {
        size_t pos = src.find(sep, offset);
        if (pos == std::string::npos)
        {
            if (offset == src.size())
                break;
            array->push_back(src.substr(offset));
            return array->size();
        }
        // 找到了分隔字符串sep
        if (pos - offset != 0)
        {
            array->push_back(src.substr(offset, pos - offset));
        }
        offset = pos + sep.size();
    }
    return array->size();
}

// 2. 读取文件所有内容，将内容放到buf中
bool Util::ReadFile(const std::string &filename, std::string *buf)
{
    // std::ios::binary
    std::ifstream ifs(filename, std::ios::binary); // 打开文件
    if (ifs.is_open() == false)
    {
        ERR_LOG("打开 %s 文件 失败", filename.c_str());
        return false;
    }

    // 打开文件成功
    // 1. 读取文件大小
    size_t fsize = 0;
    ifs.seekg(0, ifs.end); // 将文件偏移移动到文件的末尾
    fsize = ifs.tellg();   // 此时偏移的位置就是文件的大小
    ifs.seekg(0, ifs.beg); // 将文件偏移移动到文件起始位置
    buf->resize(fsize);
    ifs.read(&(*buf)[0], fsize);
    if (ifs.good() == false) // 判断上层操作是否有问题
    {
        DBG_LOG("读取 %s 文件 失败！！", filename.c_str());
        ifs.close();
    }
    ifs.close();
    return true;
}

// 3. 向文件写入数据
bool Util::WriteFile(const std::string &filename, const std::string &buf)
{
    std::ofstream ofs(filename, std::ios::binary | std::ios::trunc); // trunc: 截断，不要文件的原有内容，既覆盖写
    if (ofs.is_open() == false)
    {
        ERR_LOG("打开 %s 文件 失败", filename.c_str());
        return false;
    }
    ofs.write(buf.c_str(), buf.size()); // 将buf中的数据写入到ofs指向的文件中
    if (ofs.good() == false)
    {
        ERR_LOG("写入 %s 文件 失败", filename.c_str());
        ofs.close();
        return false;
    }
    ofs.close();
    return true;
}

// 4. URL编码
std::string Util::UrlEncode(const std::string &url, bool convert_space_to_plus)
{
    std::string res;
    for (auto c : url)
    {
        if (c == '.' || c == '-' || c == '~' || isalnum(c))
        {
            res += c;
            continue;
        }
        if (c == ' ' && convert_space_to_plus)
        {
            res += '+';
            continue;
        }
        // 剩下的字符要变成%HH的格式
        char tmp[4] = {0};
        snprintf(tmp, 4, "%%%02x", c);
        res += tmp;
    }
    return res;
}

char Util::HEXTOI(char c)
{
    if (c >= '0' && c <= '9') // 如果c是数字字符(0-9),那么c-'0' 会返回0 到 9 之间的整数值
        return c - '0';
    else if (c >= 'a' && c <= 'z') // 如果 c 是小写字母（a-f），那么c-'a'+10 会返回10 到15直接的整数值
        return c - 'a' + 10;
    else if (c >= 'A' && c <= 'Z') // 如果c是大写字母（A-F），那么c-'A'+10 会返回10到15之间的整数值
        return c - 'A' + 10;
    return -1; // 如果不是有效的十六进制字符，返回-1
}

// 5. URL解码
std::string Util::UrlDecode(const std::string url, bool convert_plus_to_space)
{
    std::string res;
    for (size_t i = 0; i < url.size(); i++)
    {
        if (url[i] == '%' && i + 2 < url.size())
        {
            char v1 = HEXTOI(url[i + 1]); // 将 urrl[i + 1] 转换为十六进制数值（既高位的数字）
            char v2 = HEXTOI(url[i + 2]); // 将 url[i+2] 转换为十六进制数值（既低位的数字）
            char v = (v1 * 16) + v2;      // 将十六进制字符合并为一个完整的字节(或字符)。十六进制是基于16的，所以：
                                          // v1 是高位，乘以 16，将其转换为一个完整的数值范围。v2 是低位，直接加上高位的结果，得到最终的字符值。
            res += v;
            i += 2;
            continue;
        }
        if (convert_plus_to_space == true && url[i] == '+')
        {
            res += ' ';
            continue;
        }
        res += url[i];
    }
    return res;
}

// 6. 响应状态码的信息获取
std::string Util::StatuDesc(int statu)
{
    auto it = _statu_msg.find(statu);
    if (it != _statu_msg.end())
    {
        return it->second;
    }
    return "Unkown";
}

// 7. 根据文件后缀名获取文件mime
std::string Util::ExtMime(const std::string &filename)
{
    size_t pos = filename.find_last_not_of('.');
    if (pos == std::string::npos)
    {
        return "application/occtet-stream";
    }
    //根据扩展名获取mime
    std::string ext = filename.substr(pos);
    auto it = _mime_msg.find(ext);
    if (it == _mime_msg.end())
    {
        return "application/occtet-stream";
    }
    return it->second;
}

// 8. 判断一个文件是否是一个目录
bool Util::IsDirectory(const std::string &filename)
{
    struct stat st;
    int ret = stat(filename.c_str(), &st); // 获取文件属性
    if (ret < 0)
    {
        return false;
    }
    return S_ISDIR(st.st_mode);
}

// 9. 判断一个文件是否是普通文件
bool Util::IsRegulay(const std::string &filename)
{
    struct stat st;
    int ret = stat(filename.c_str(), &st);
    if (ret < 0)
    {
        return false;
    }
    return S_ISREG(st.st_mode);
}

// 10. http请求的资源路径有效性判断
bool Util::ValidPath(const std::string &path)
{
    std::vector<std::string> subdir;
    Util::Split(path, "/", &subdir);
    int level = 0;
    for (auto dir : subdir)
    {
        if (dir == "..")
        {
            level--;
            if (level < 0)
            {
                return false;
            }
        }
        level++;
    }
    return true;
}